Ethereum 系统架构梳理
来源: ethereum.org · 更新: 2024
1. 系统架构总览
Ethereum 采用双层架构:执行层(Execution Layer)处理交易和执行智能合约,共识层(Consensus Layer / Beacon Chain)负责区块排序和 PoS 共识。两层通过 Engine API 通信。
1.1 双层架构总览
(Merkle Patricia Trie)")] JSONRPC --> TxPool TxPool --> EVM EVM --> StateDB end subgraph CL["🔗 共识层 (Consensus Layer / Beacon Chain)"] direction TB FC["分叉选择 (LMD-GHOST)"] FFG["最终确定性 (Casper FFG)"] RANDAO["RANDAO 随机数"] Validators["验证者管理"] end subgraph Net["🌐 P2P 网络"] p2p["libp2p / devp2p"] end Wallet -->|"签名交易"| JSONRPC DApp -->|"eth_call / eth_sendTransaction"| JSONRPC JSONRPC <-->|"Engine API
newPayload / forkchoiceUpdated"| FC EL <-->|"区块 & 交易 gossip"| p2p CL <-->|"见证 & 区块 gossip"| p2p
1.2 核心数据流
验证 & 入 Mempool"] B --> C["区块提议者
打包交易"] C --> D["执行交易
生成新状态根"] D --> E["共识客户端
封装 Beacon Block"] E --> F["P2P 广播"] F --> G["其他验证者
重新执行 & 验证"] G --> H["投见证票
(Attestation)"] H --> I["达到 2/3 多数"] I --> J["区块最终确定
(Finalized)"]
2. 账户模型
Ethereum 有两种账户类型:外部账户 (EOA) 由私钥控制,合约账户 由代码控制。每个账户是一个包含 4 个字段的状态对象。
2.1 账户结构
2.2 地址派生
64 hex"] --> B["ECDSA secp256k1"] B --> C["公钥
128 hex (未压缩)"] C --> D["Keccak-256 哈希"] D --> E["取后 20 字节"] E --> F["地址
0x + 40 hex"] G["创建者地址 + nonce"] --> H["RLP 编码"] H --> I["Keccak-256"] I --> J["取后 20 字节"] J --> K["合约地址
0x + 40 hex"]
3. 交易生命周期
一笔交易从用户签名到最终确定,经历 Mempool、执行、区块打包、共识投票、最终确定等阶段。
3.1 交易字段
3.2 交易生命周期时序
当前 slot 的区块提议者 Prop->>MP: 7. 从 Mempool 拉取交易 Prop->>EL: 8. 执行交易,生成新的状态根 Prop->>CL: 9. 封装 Beacon Block CL->>CL: 10. gossip 到共识网络 Att->>EL: 11. 重新执行交易验证 Att->>CL: 12. 投见证票 (Attestation) Note over CL,Att: 13. 一个 epoch (32 slots) 内
收集超 2/3 见证票 CL->>CL: 14. Casper FFG: 检查点 finalize Note over U: 交易最终确定 ✅
3.3 Gas 费用模型 (EIP-1559)
4. ERC-20 代币机制
Ethereum 协议层只认 ETH——账户的 balance 字段只记录该地址的 ETH 余额。USDT、USDC、DAI 等代币并非原生的,而是由 ERC-20 合约的 storage 维护的账本。
用户"转账 USDT",是发送一笔 value=0 ETH 的交易到 USDT 合约地址——用户不需要持有 ETH,只需要少量 ETH 付 gas 费。to 填的是合约地址而非收款方,收款方和金额都编码在交易的 data 字段里。
4.1 原生 ETH 转账 vs ERC-20 代币转账
to: 用户B
value: 1 ETH
data: 0x (空)"] N2["EVM 直接从 A.balance -= 1
B.balance += 1"] end subgraph Token["ERC-20 代币转账"] T1["from: 用户A
to: USDT合约 (0xdAC1...)
value: 0 ETH
data: 0xa9059cbb...
└─ transfer(address,uint256) 的编码"] T2["EVM 调用 USDT 合约 transfer()"] T3["合约 storage 更新:
balanceOf[A] -= 100
balanceOf[B] += 100"] end N1 --> N2 T1 --> T2 --> T3
4.2 data 字段编码 (ABI Encoding)
函数选择器
0xa9059cbb"] Param1["32 字节
recipient (address)
0x000...接收地址"] Param2["32 字节
amount (uint256)
0x000...64"] end Step1["transfer(address recipient, uint256 amount)"] -->|"Keccak256 前 4 字节"| Selector Step2["接收地址 20 字节"] -->|"左侧补 0 至 32 字节"| Param1 Step3["金额 100 (USDT 6 位小数) = 100000000"] -->|"uint256 编码"| Param2
4.3 ERC-20 合约标准接口
4.4 approve + transferFrom 授权流程 (DeFi 基础)
balanceOf[DeFi] += 1000 Token-->>DeFi: return true Note over U,DeFi: 所以往 Aave 存 USDT 需要两笔交易:
1. approve USDT 给 Aave
2. supply 到 Aave
4.5 代币余额查询 (eth_call)
必须调用合约的 balanceOf() Wallet->>Node: eth_call
to: USDT合约
data: balanceOf(用户地址) Node->>Node: 执行 balanceOf()
从合约 storage 读取 Node-->>Wallet: 0x000...000064 (100 USDT) Note over Wallet,Node: eth_call 是只读查询
不消耗 Gas,不上链
4.6 UserA 转账 10 USDT 给 UserB 完整时序
0xdAC17F958D2ee523a2206206994597C13D831ec7 participant Miner as 区块提议者 participant Chain as Ethereum 链 Note over A,B: UserA 要给 UserB 转 10 USDT Note over W: 钱包维护 token名→合约地址 映射:
"USDT" → 0xdAC17F958D2ee523a2206206994597C13D831ec7
用户看到的 "USDT" 只是钱包 UI 的别名 A->>W: 输入 UserB 地址 + 10 USDT,点发送 W->>W: 钱包查映射表,把 "USDT" 翻译成
合约地址 0xdAC17F...
构造交易对象:
from: UserA
to: USDT合约地址 (0xdAC17F...)
value: 0 ETH
data: encode(transfer(UserB, 10*10^6)) W->>W: 估算 Gas Limit (~50,000) W->>A: 展示 Gas 费用,请确认 A->>W: 确认签名 W->>W: 用 UserA 私钥 ECDSA 签名 W->>Node: eth_sendRawTransaction(签名后的交易) Node->>Node: 验证: 签名 ✓ nonce ✓ ETH 余额够付 gas ✓ Node->>Node: 入本地 Mempool Node-->>Node: gossip 到其他节点 Miner->>Node: 从 Mempool 取出此交易 Miner->>USDT: CALL transfer(UserB, 10,000,000) USDT->>USDT: require(balanceOf[UserA] >= 10,000,000) USDT->>USDT: balanceOf[UserA] -= 10,000,000 USDT->>USDT: balanceOf[UserB] += 10,000,000 USDT-->>Miner: emit Transfer(UserA, UserB, 10,000,000) USDT-->>Miner: return true Miner->>Miner: 结算 Gas = GasUsed × (BaseFee + Tip) Miner->>Miner: 封装区块,广播 Chain->>Chain: 见证委员会验证 → Finalize Note over A,B: UserB 查看钱包: +10 USDT ✓
整个过程 UserB 无需任何操作
5. EVM 执行模型
EVM 是一个基于栈的虚拟机,栈深度 1024,每个元素为 256-bit 字。编译后的智能合约字节码被解析为 Opcodes 逐条执行。
5.1 EVM 内部结构
深度 1024 × 256-bit"] Memory["暂存内存
(Volatile, 交易间不持久)"] TStore["瞬态存储
(TSTORE/TLOAD, 跨内部调用)"] Gas["Gas 计数器"] end subgraph World["外部世界状态"] AccountInfo["账户信息
(Balance, Nonce)"] BlockInfo["区块信息
(Timestamp, Number, GasLimit)"] Storage["持久存储
(SSTORE/SLOAD, 写入全局状态)"] end subgraph Bytecode["合约字节码"] Opcodes["PUSH1, ADD, SSTORE, CALL, RETURN..."] end PC --> Opcodes Opcodes -->|"操作数"| Stack Opcodes -->|"读写"| Memory Opcodes -->|"TSTORE/TLOAD"| TStore Opcodes -->|"SSTORE/SLOAD"| Storage Opcodes -->|"BALANCE/CALL"| AccountInfo Opcodes -->|"TIMESTAMP/NUMBER"| BlockInfo Opcodes -->|"消耗"| Gas
5.2 状态转换函数
(World State Trie)"] T["交易列表 T"] end subgraph Transition["状态转换 Y"] Y["Y(S, T) = S'"] end subgraph Outputs["输出"] S2["新状态 S'
(New State Root)"] Receipts["交易收据
(Logs, Gas Used)"] end Inputs --> Transition Transition --> Outputs
5.3 合约调用机制
Gas 限制 63/64 规则
6. PoS 共识流程 (Gasper)
Ethereum 的共识协议称为 Gasper,由两部分组成:Casper FFG(最终确定性工具)和 LMD-GHOST(分叉选择规则)。时间以 Slot(12 秒)和 Epoch(32 个 Slot,6.4 分钟)为单位。
6.1 Slot & Epoch 时间结构
(Checkpoint)"] --> S1["Slot 1"] --> Dots["..."] --> S31["Slot 31"] end S31 -->|"下一个 Epoch"| S32["Slot 32
(Checkpoint)"]
6.2 区块提议 & 见证流程
伪随机选出所有 Slot 的
提议者和委员会 R->>Prop: 选中为当前 Slot 提议者 Prop->>Prop: 从执行层获取 Execution Payload Prop->>Prop: 封装 Beacon Block Prop->>Chain: 广播 Beacon Block R->>Com: 选出当前 Slot 见证委员会 Chain->>Com: 接收 Beacon Block Com->>Com: 重新执行验证 Com->>Chain: 投票 (Attestation) Note over Com: 投票包含:
1. 当前 Head 投票 (LMD-GHOST)
2. 源 Checkpoint 投票 (FFG)
3. 目标 Checkpoint 投票 (FFG)
6.3 Casper FFG 最终确定性
验证目标 Checkpoint Justified --> Finalized: 下一个 Epoch
再次超过 2/3 投票 Finalized --> [*] note right of Justified: 可能回滚 note right of Finalized: 不可回滚
需销毁 ≥1/3 总质押 ETH
6.4 LMD-GHOST 分叉选择
最高的分叉"] Note["每次只考虑
验证者的最新消息
(Latest Message)"]
6.5 验证者奖惩机制
7. 状态存储:Merkle Patricia Trie
Ethereum 的全局状态存储在一个 Modified Merkle Patricia Trie (MPT) 中。每个区块头包含 stateRoot,唯一标识该区块时刻的完整世界状态。
7.1 三层 Trie 结构
Key: 地址 → Value: 账户状态"] WorldState -->|"storageRoot"| StorageTrie1["合约 A 存储 Trie
Key: 存储槽位 → Value: 值"] WorldState -->|"storageRoot"| StorageTrie2["合约 B 存储 Trie"] subgraph TrieNode["Trie 节点类型"] Leaf["叶子节点
存实际 value"] Ext["扩展节点
存共享前缀"] Branch["分支节点
17 个子节点 (16 hex + 1 value)"] end
7.2 状态根更新流程
被重新计算哈希
未被修改的节点共享 State-->>EVM: 返回新的存储根 EVM->>EVM: 更新账户 storageRoot Note over State: 最终生成新的
世界状态根 (stateRoot)
写入区块头
8. 节点架构
Ethereum 节点由执行客户端和共识客户端两个独立进程组成,通过本地 Engine API 通信。一个完整的验证者节点还需要运行验证者客户端。
8.1 节点组件
newPayload
forkchoiceUpdated"| CL_Engine V_Duties --> CL_Engine end EL_P2P <-->|"交易 & 区块 gossip"| Network["Ethereum P2P 网络"] CL_P2P <-->|"见证 & Beacon Block gossip"| Network
8.2 Engine API 核心方法
8.3 主流客户端实现
Ethereum System Architecture · 基于 ethereum.org 官方文档 · 2024